//
//  DNBaseViewModel.swift
//  LunalerApp
//
//  Created by 露乐ios on 2018/3/7.
//  Copyright © 2018年 露乐ios. All rights reserved.
//

import UIKit


@objc protocol DNViewModelProtocol{
  @objc optional func updateValue(value:AnyObject,withModel:AnyObject,target:AnyObject)->Void
  @objc optional func handleActionWithViewModel(viewModel:AnyObject,target:AnyObject)->Void
    
}


public typealias reactBlock = (AnyObject ,DNBaseViewModel) -> ()
typealias enumerateBlock = (DNViewModelProtocol) -> Void


@objcMembers public class DNBaseViewModel: NSObject {
    
    var binderDict = NSMutableDictionary()
    
    /// 观察对象代理集合
    var delegates  = NSMutableArray()
    
    /// 观察对象属性回调的block
    var binderReactBlockDict  = NSMutableDictionary()
//    观察的Keypath
    var observerKeyPaths = NSMutableArray()
    
    deinit {
        for keypath in self.observerKeyPaths {
            self.removeObserver(self, forKeyPath: (keypath as! NSString) as String)
        }
    }
    
    func initWithDelegate(delegate:AnyObject) -> Self{
        registerDelegate(delegate: delegate)
        return self
    }
    
//    class func dninitWithDelegate(delegate:AnyObject) -> DNBaseViewModel{
//        debugPrint("class coder info:\(self.classForCoder())")
//        let viewModel = self.classForCoder().alloc().initWithDelegate(delegate: delegate)
////        viewModel.initWithDelegate(delegate: delegate)
////        viewModel.registerDelegate(delegate: delegate)
//        return viewModel as! DNBaseViewModel
//    }
    
    func bindProperty(propertyName:NSString,target:AnyObject) -> Void {
        if propertyName.length > 0 {
            let weakObject : DNWeakProxy = DNWeakProxy.alloc()
            weakObject.target = target
            if (self.binderDict.object(forKey: propertyName) != nil){
                let weakObjects = self.binderDict.object(forKey: propertyName) as! NSMutableArray
                weakObjects.add(weakObject)
            }else{
                let weakObjects = NSMutableArray.init(object: weakObject)
                self.binderDict.addEntries(from: [propertyName:weakObjects])
                if self.binderReactBlockDict.object(forKey: propertyName) == nil{
                self.observerKeyPaths.add(propertyName)
                self.addObserver(self, forKeyPath: propertyName as String, options: NSKeyValueObservingOptions(rawValue: NSKeyValueObservingOptions.RawValue(UInt8(NSKeyValueObservingOptions.new.rawValue) | UInt8(NSKeyValueObservingOptions.old.rawValue))), context: nil)
                }
            }
        }
    }
    
    
    
    func bindProperty(propertyName:NSString,block:reactBlock) -> Void {
        if propertyName.length > 0 {
            if (self.binderReactBlockDict.object(forKey: propertyName) != nil){
                let weakObjects = self.binderReactBlockDict.object(forKey: propertyName) as! NSMutableArray
                weakObjects.add(block)
            }else{
                let weakObjects = NSMutableArray.init(object: block)
                self.binderReactBlockDict.addEntries(from: [propertyName:weakObjects])
                if self.binderDict.object(forKey: propertyName) == nil{
                    self.observerKeyPaths.add(propertyName)
                    self.addObserver(self, forKeyPath: propertyName as String, options: NSKeyValueObservingOptions(rawValue: NSKeyValueObservingOptions.RawValue(UInt8(NSKeyValueObservingOptions.new.rawValue) | UInt8(NSKeyValueObservingOptions.old.rawValue))), context: nil)
                    
                }
            }
        }
    }
    
    
    func unbindProperty(propertyName:NSString , target:AnyObject) -> Void {
        if propertyName.length > 0 {
            if self.binderDict.object(forKey: propertyName) != nil {
                let weakObjects = self.binderDict.object(forKey: propertyName) as! NSMutableArray
                for weakobject in weakObjects {
                    let weakObj = weakobject as! DNWeakProxy
                    if weakObj.target == target as! _OptionalNilComparisonType {
                        weakObjects.remove(weakobject)
                        break
                    }
                }
                
                if weakObjects.count == 0 {
                    self.binderDict.removeObject(forKey: propertyName)
                    if self.binderReactBlockDict.object(forKey: propertyName) == nil{
                        self.removeObserver(self, forKeyPath: propertyName as String)
                        for Keypath in self.observerKeyPaths{
                            if (Keypath as! NSString).isEqual(to: propertyName as String){
                                self.observerKeyPaths.remove(Keypath)
                                break
                            }
                        }
                    }
                }
            }
        }
    }
    
    
    func unbindProperty(propertyName:NSString ,block: reactBlock){
        if propertyName.length > 0 {
            if (self.binderReactBlockDict.object(forKey: propertyName) != nil){
                let weakObjects = self.binderReactBlockDict.object(forKey: propertyName) as! NSMutableArray
                for weakObject in weakObjects {
                    let weakObj = weakObject as AnyObject
                    if weakObj.isEqual(block){
                        weakObjects.remove(weakObject)
                        break
                    }
                }
                
                if weakObjects.count == 0 {
                    self.binderReactBlockDict.removeObject(forKey: propertyName)
                    if self.binderDict.object(forKey: propertyName) != nil {
                        self.removeObserver(self, forKeyPath: propertyName as String)
                        for keyPath in self.observerKeyPaths{
                            if propertyName.isEqual(to: (keyPath as! NSString) as String){
                                self.observerKeyPaths.remove(keyPath)
                                break
                            }
                        }
                    }
                }
            }
        }
    }
    
    
    func updateProperty(propertyName:AnyObject,Value:AnyObject) -> Void {
        if self.responds(to: NSSelectorFromString(propertyName as! String)) == false{
            return
        }
        if (self.binderDict.object(forKey: propertyName) != nil) || (self.binderReactBlockDict.object(forKey: propertyName) != nil) {
            self.removeObserver(self, forKeyPath: propertyName as! String)
            self.setValue(Value, forKey: propertyName as! String)
            self.addObserver(self, forKeyPath: propertyName as! String, options: NSKeyValueObservingOptions(rawValue: NSKeyValueObservingOptions.RawValue(UInt8(NSKeyValueObservingOptions.new.rawValue) | UInt8(NSKeyValueObservingOptions.old.rawValue))), context: nil)
        } else {
            self.setValue(Value, forKey: propertyName as! String)
        }
        
    }
    
    public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        for propertyName in self.observerKeyPaths {
            if (keyPath! as NSString).isEqual(to: propertyName as! String){
                let value = self.value(forKey: propertyName as! String) as AnyObject
                if self.binderDict.object(forKey: propertyName) != nil{
                    let weakObjects : [DNWeakProxy] = self.binderDict.object(forKey: propertyName) as! [DNWeakProxy]
                    for weakObject in weakObjects{
                        self.enumerateDelegatesUsingBlock(block: { (delegate:AnyObject) in
                            if delegate.responds(to: #selector(DNViewModelProtocol.updateValue(value:withModel:target:))){
                                delegate.updateValue!(value: value, withModel: self, target: weakObject.target!)
                            }
                        })
                    }
                }
                
                if self.binderReactBlockDict.object(forKey: propertyName) != nil {
                    let weakReactBlocks : [reactBlock] = self.binderReactBlockDict.object(forKey: propertyName) as! [reactBlock]
                    for block in weakReactBlocks{
                        block(value,self)
                    }
                    
                }
            }
        }
    }
    
    func bindActionWithTarget(target:AnyObject){
        self.enumerateDelegatesUsingBlock { (delegate:AnyObject) in
            if delegate.responds(to: #selector(DNViewModelProtocol.handleActionWithViewModel(viewModel:target:))){
                delegate.handleActionWithViewModel!(viewModel: self, target: target)
            }
        }
    }
    
    func registerDelegate(delegate:AnyObject){
        let weakProxy : DNWeakProxy = DNWeakProxy.alloc()
        weakProxy.target = delegate
        self.delegates.add(weakProxy)
    }
    
    func unregisterDelegate(delegate:AnyObject){
        let removedIndexSet = NSMutableIndexSet()
        var index = 0
        for weakProxy in delegates {
            let tempWeakProxy = weakProxy as! DNWeakProxy
            let tempDelegate = tempWeakProxy.target
            if tempDelegate == delegate as! _OptionalNilComparisonType{
                removedIndexSet.add(index);
            }
            index+=1;
        }
        self.delegates.removeObjects(at: removedIndexSet as IndexSet)
    }
    
    
    func enumerateDelegatesUsingBlock(block:enumerateBlock)  {
        let delegates = self.delegates.copy() as! NSArray
        for proxy in delegates {
            let weakProxy = proxy as! DNWeakProxy
            let delegate = weakProxy.target
            if delegate != nil {
                block(delegate as! DNViewModelProtocol)
            }
        }
    }
    
}


@objcMembers public class DNWeakProxy : NSProxy{
    
    
     var target : AnyObject?
    
//    public func initWithTarget(target:AnyObject) -> DNWeakProxy {
//        self.target = target
//        return self
//    }
//
//    public func proxyWithTarget(target:AnyObject) -> DNWeakProxy{
//        return self.initWithTarget(target:target)
//    }
    
    public override func perform(_ aSelector: Selector!) -> Unmanaged<AnyObject>! {
        return self.target as! Unmanaged<AnyObject>
    }
    
    public override func isProxy() -> Bool {
        return true
    }
    
    public override var description: String{
        return (self.target?.description)!
    }
    
    public override var debugDescription: String{
        return self.target.debugDescription
    }
    
    public override func conforms(to aProtocol: Protocol) -> Bool {
        return (self.target?.conforms(to:aProtocol))!
    }
    
    public override func isMember(of aClass: AnyClass) -> Bool {
        return (self.target?.isMember(of:aClass))!
    }
    
    public override func isKind(of aClass: AnyClass) -> Bool {
        return (self.target?.isKind(of:aClass))!
    }
    
    public override var superclass: AnyClass?{
        return self.target?.superclass
    }
    
    public override var hash: Int{
        return (self.target?.hash)!
    }
    
    public override func isEqual(_ object: Any?) -> Bool {
        return (self.target?.isEqual(_:object))!
    }
    
    public override func responds(to aSelector: Selector!) -> Bool {
        return (self.target?.responds(to:aSelector))!
    }
}

